Important
Variable and functions should be defined into object, class, function. You can’t define them on the root of a Scala file.
Basics
Types
In Scala, there is 5 major types
Type |
Literal |
Description |
|---|---|---|
Boolean |
true, false |
|
Int |
3, 0x32 |
32 bits integer |
Float |
3.14f |
32 bits floating point |
Double |
3.14 |
64 bits floating point |
String |
“Hello world” |
UTF-16 string |
Variable
In scala, you can define variable by using the var keyword :
var number : Int = 0
number = 6
number += 4
println(number) // 10
Scala is able to infer the type automatically. You don’t need to specify it if the variable is assigned at declaration:
var number = 0 //The type of 'number' is inferred as a Int during the compilation.
But in fact, it’s not very common to use var in Scala. In place of var, the val is very often used. val allow you to define a constant value :
val two = 2
val three = 3
val six = two * three
Function
For example, if you want to define a function which return true if the sum of its two arguments is bigger than zero, you can do as following :
def sumBiggerThanZero(a: Float, b: Float): Boolean = {
return (a + b) > 0
}
Then to call this function you can do as following :
sumBiggerThanZero(2.3f, 5.4f)
You can also specify arguements by name, which is useful if you have many arguements :
sumBiggerThanZero(
a = 2.3f,
b = 5.4f
)
Return
The return keyword is not necessary. In absence of it, Scala take the last statement of your function as returned value.
def sumBiggerThanZero(a: Float, b: Float): Boolean = {
(a + b) > 0
}
Return type inferation
Scala is able to automatically infer the return type. You don’t need to specify it :
def sumBiggerThanZero(a: Float, b: Float) = {
(a + b) > 0
}
Curly braces
Scala function doesn’t require to have curly braces if your function contain only one statement :
def sumBiggerThanZero(a: Float, b: Float) = (a + b) > 0
Function that return nothing
If you want a function to return nothing, the return type should be set to Unit. It’s equivalent to the C/C++ void.
def printer(): Unit = {
println("1234")
println("5678")
}
Arguements default value
You can specify a default value to each arguements of a function :
def sumBiggerThanZero(a: Float, b: Float = 0.0f) = {
(a + b) > 0
}
Apply
Functions named apply are special because you can call them without having to type their name :
class Array(){
def apply(index: Int): Int = index + 3
}
val array = new Array()
val value = array(4) //array(4) is interpreted as array.apply(4) and will return 7
This concept is also applicable for scala object (static)
object MajorityVote{
def apply(value: Int): Int = ...
}
val value = MajorityVote(4) // Will call MajorityVote.apply(4)
Object
In scala, there is no static keyword. In place of that, there is object. Everything defined into an object is static.
The following example define a static function named pow2 which take as parameter an floating point value and return a floating point as well.
object MathUtils{
def pow2(value: Float): Float = value*value
}
Then you can call it by writing :
MathUtils.pow2(42.0f)
Entry point (main)
The entry point of a Scala program (the main function) should be defined into an object as a function named main.
object MyTopLevelMain{
def main(args: Array[String]) {
println("Hello world")
}
}
Class
The class syntax is very similar to the Java one. Imagine you want to define an Color class which take as construction parameter three Float value (r,g,b) :
class Color(r: Float, g: Float, b: Float){
def getGrayLevel(): Float = r * 0.3f + g * 0.4f + b *0.4f
}
Then to instantiate a the class from the previous example and use its gray function :
val blue = new Color(0, 0, 1)
val grayLevelOfBlue = blue.getGrayLevel()
Be careful, if you want to access a construction parameter of the class from the outside, this construction parameter should be defined as a val :
class Color(valr : Float, val g: Float, val b: Float){ ... }
...
val blue = new Color(0, 0, 1)
val redLevelOfBlue = blue.r
Inheritance
As an example, imagine you want to define an class Rectangle and a class Square which extends the class Shape :
class Shape{
def getArea(): Float
}
class Square(sideLength: Float) extends Shape {
override def getArea() = sideLength * sideLength
}
class Rectangle(width: Float, height: Float) extends Shape {
override def getArea() = width * height
}
Case class
Case class is an alternative way of declaring classes.
case class Rectangle(width: Float, height: Float) extends Shape {
override def getArea() = width * height
}
Then there is some differences between case class and class :
case class doesn’t need the
newkeyword to be instantiatedconstruction parameters are accessible from the outside, you don’t need to define them as
val.
In SpinalHDL, for some reason explains into the coding conventions, it’s in general recommended to use case class instead of class to have less typing and more coherency.
Templates / Type parameterization
Imagine you want to design a class which is a queue of a given datatype, in that case you need to provide a type parameter to the class :
class Queue[T](){
def push(that: T) : Unit = ...
def pop(): T = ...
}
If you want to restrict the T type to be a sub class of a given type (for example Shape), you can use the <: Shape syntax :
class Shape() {
def getArea(): Float
}
class Rectangle() extends Shape { ... }
class Queue[T <: Shape](){
def push(that: T): Unit = ...
def pop(): T = ...
}
The same is possible for functions :
def doSomething[T <: Shape](shape: T): Something = { shape.getArea() }